from _datetime import datetime
import random
import re

from flask import abort, jsonify
from flask import request
from flask import current_app
from flask import session

from info.libs.yuntongxin.sms import CCP
from info.models import User
from info.modules.passport import passport_bp
from info.utils.captcha.captcha import captcha
from info import redis_store, db
from info import constants
from flask import make_response


from info.utils.response_code import RET


@passport_bp.route('/image_code')
def get_image_code():
    """获取验证码图片的后端接口"""

    """
    1. 获取参数
        1.1 获取code_id，
    2. 校验参数
        2.1 非空判断， 判断code_id是否有值
    3. 逻辑处理
        3.1 生成图片验证码，名字，内容，图片
        3.2 以code_id为key保存验证码图片的内容到redis数据库里面
    4. 返回值
        4.1 返回验证码图片
    """

    # 1.1获取code_id，
    code_id = request.args.get('code_id', '')
    # 2.1非空判断， 判断code_id是否有值
    if not code_id:
        current_app.logger.error("参数不足")
        # 参数不存在  404错误
        abort(404)
    # 3.1生成图片验证码，名字，内容，图片
    name, text, image = captcha.generate_captcha()
    # 3.2 以code_id为key保存验证码图片的内容到redis数据库里面
    try:
        redis_store.setex('cimg' + code_id, constants.IMAGE_CODE_REDIS_EXPIRES, text)
    except Exception as e:
        current_app.logger.error(e)
        abort(500)
    # 4.1 返回验证码图片
    response = make_response(image)
    response.headers["Content-Type"] = "image/jpg"
    return response


@passport_bp.route('/sms_code', methods=['POST'])
def send_sms_code():
    """点击发送短信验证码后端接口"""

    """
    1. 获取参数
        1.1 图片验证码的id
        1.2 用户输入的验证码内容
        1.3 用户的手机号码
    2. 校验参数
        2.1 非空判断
        2.2 手机格式是否正确
    3. 逻辑处理
        3.1 根据图片验证码的Id去redis数据库里面查找图片验证码的值
            3.1.1 有值就从redis里面删除（防止其他人多次拿来验证）
            3.1.2 没有值就代表图片验证码过期了
        3.2 拿着用户的输入值和查出来的进行比较
        3.3 不相等就告诉前端说验证码验证错误
        3.4 相等 生成随机六位数字的短信验证码内容
        3.5 将短信验证码的内容保存到redis中
    4. 返回值
        4.1 发送短信验证码成功

    """
    # 1.1 接收参数
    param_dict = request.json
    mobile = param_dict['mobile']
    image_code = param_dict['image_code']
    image_code_id = param_dict['image_code_id']

    # 2.1 非空判断
    if not all([mobile, image_code, image_code_id]):
        current_app.logger.error("参数全")
        # 给前端返回json格式的错误信息
        return jsonify(errno=RET.PARAMERR, errmsg="参数不足")
    # 2.2 手机格式是否正确
    if not re.match('1[35678]\d{9}', mobile):
        current_app.logger.error("手机格式不正确")
        return jsonify(errno=RET.PARAMERR, errmsg="手机格式不正确")

    # 3.1 根据图片验证码的Id去redis数据库里面查找图片验证码的值
    try:
        real_image_code = redis_store.get('cimg' + image_code_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="获取不到redis")

    # 3.1.1 有值就从redis里面删除（防止其他人多次拿来验证）
    if real_image_code:
        redis_store.delete('cimg' + image_code_id)

    # 3.1.2 没有值就代表图片验证码过期了
    else:
        return jsonify(errno=RET.NODATA, errmsg="图片验证码过期")

    # 3.2 拿着用户的输入值和查出来的进行比较
    # 3.3 不相等就告诉前端说验证码验证错误
    if image_code.lower() != real_image_code.lower():
        return jsonify(errno=RET.DATAERR, errmsg="填写图片验证码错误")
    # 判断用户是否注册过，注册过就不发短信了，提高用户体验
    try:
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAEXIST, errmsg="手机号码已经注册")
    # 3.4 相等 生成随机六位数字的短信验证码内容
    sms_code = random.randint(0, 999999)
    sms_code = "%06d" % sms_code
    print(sms_code)

    # 发短信， 发送成功会返回一个0   否则表示发送失败
    # try:
    #     result = CCP().send_template_sms(mobile, {sms_code, constants.SMS_CODE_REDIS_EXPIRES / 60}, 1)
    # except Exception as e:
    #     current_app.logger.error(e)
    #     return jsonify(errno=RET.THIRDERR, errmsg="云通讯发送短信失败")
    #
    # if result != 0:
    #     return jsonify(errno=RET.THIRDERR, errmsg="云通讯发送短信失败")

    try:
        # 把短信验证码的内容保存起来 注册的时候会用到来验证
        redis_store.setex("SMS" + mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code)

    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="存储短信验证码异常")

    # 4.  返回值
    return jsonify(errno=RET.OK, errmsg="发送短信验证码成功，请注意查收")


@passport_bp.route('/register', methods=['POST'])
def register():
    """注册的后端接口"""

    # 1.获取参数
    param_dict = request.json
    mobile = param_dict.get('mobile', '')
    sms_code = param_dict.get('smscode', '')
    password = param_dict.get('password', '')

    # 2.1 非空判断
    if not all([mobile, sms_code, password]):
        return jsonify(errno=RET.PARAMERR, errmsg="参数不足")

    # 2.2 手机号码格式判断
    if not re.match('1[356789][0-9]{9}', mobile):
        current_app.logger.error("手机格式错误")
        return jsonify(errno=RET.PARAMERR, errmsg="手机格式错误")
    # 3.1 去redis里面找出真实的短信验证码值
    try:
        real_sms_code = redis_store.get("SMS" + mobile)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg="查询真实短信验证码异常")

    # 从redis把验证码删除
    if real_sms_code:
        redis_store.delete("SMS" + mobile)
    else:
        return jsonify(errno=RET.NODATA, errmsg="短信验证码过期")
    # 3.2 对比用户填写的短信验证码和真实的验证码
    if sms_code != real_sms_code:
        return jsonify(errno=RET.DATAERR, errmsg="短信验证码填写错误")

    # 3.3 相等， 创建User对象并赋值存到mysql
    user = User()
    user.mobile = mobile
    user.nick_name = mobile
    # 保存最后异常登录的时间
    user.last_login = datetime.now()

    # 密码加密处理 这里赋值的底层实现是通过@property这个装饰器
    # 设置的时候会加密再存到self.password_hash
    user.password = password


    # 3.4 存储到mysql
    try:
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg="存储错误")

    # 3.5 注册成功后一般自动登录， 记录用户登录信息
    session["user_id"] = user.id
    session["nick_name"] = user.mobile
    session["mobile"] = user.mobile

    return jsonify(errno=RET.OK, errmsg="注册成功")


@passport_bp.route('/login', methods=['POST'])
def login():
    """用户登录后端接口  POST"""

    """
    1.获取参数
        1.1 mobile:手机号码  password:未加密的密码
    2.参数校验
        2.1 非空判断
        2.2 手机号码格式判断
    3.逻辑处理
        3.1 根据mobile查询用户是否存在
        3.2 不存在：提示账号不存在
        3.3 存在：判断密码是否填写正确
        3.4 保存用户登录信息（更新用户最后一次登录时间）
    4.返回值
        4.1 登录成功
    """

    # 1.1 获取参数
    param_dict = request.json
    mobile = param_dict.get('mobile')
    password = param_dict.get('password')
    # 2.1 非空判断
    if not all([mobile, password]):
        return jsonify(errno=RET.PARAMERR, errmsg="参数不足")

    # 2.2 手机号码格式判断
    if not re.match('1[356789][0-9]{9}', mobile):
        return jsonify(errno=RET.PARAMERR, errmsg="手机格式错误")

    # 3.1 根据mobile查询用户是否存在了
    try:
        user = User.query.filter(User.mobile==mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="查询用户异常")

    # 3.2 不存在提示用户不存在
    if not user:
        return jsonify(errno=RET.NODATA, errmsg="用户不存在")

    # 3.3 存在验证密码是否正确
    if not user.check_passowrd(password):
        # 密码错误
        return jsonify(errno=RET.DATAERR, errmsg="密码填写错误")

    # 3.4 保存用户登录信息
    session['user_id'] = user.id
    session['nick_name'] = user.mobile
    session['mobile'] = user.mobile

    # 更新最后一次登录的时间
    user.last_login = datetime.now()

    # 修改了对象属性之后要commit保存到数据库
    try:
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg="保存用户数据异常")
    # 4 登录成功
    return jsonify(errno=RET.OK, errmsg="登录成功")


@passport_bp.route('/logout', methods=['post'])
def logout():
    """退出登录后端接口"""
    # 将洪湖登录信息清楚

    session.pop("user_id", None)
    session.pop("mobile", None)
    session.pop("nick_name", None)
    session.pop("is_admin", None)
    return jsonify(errno=RET.OK, errmsg="退出登录成功")

































